package com.yangrd.helper.crudhelper.core.crud.page.impl;

import com.yangrd.helper.crudhelper.core.crud.page.CrudPage;
import com.yangrd.helper.crudhelper.core.crud.page.CrudPageRepository;
import com.yangrd.helper.crudhelper.core.crud.page.DomainMappingManager;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.List;
import java.util.Optional;

/**
 * description: JDBCMemoryCrudPageRepository
 *
 * @author: yangrd
 * @date: 2020/10/24
 * @version: 1.0
 */
@Slf4j

@RequiredArgsConstructor
public class JdbcCrudPageRepository implements CrudPageRepository {

    private final JdbcTemplate jdbcTemplate;

    private static final String INIT_TABLE_SQL = "CREATE TABLE If Not Exists `t_crud_page` (\n" +
            "  `domain_class_name` varchar(255) COLLATE utf8_bin NOT NULL,\n" +
            "  `config` blob NOT NULL,\n" +
            "  PRIMARY KEY (`domain_class_name`)\n" +
            ");";

    private static final String SELECT_ONE_SQL = "SELECT domain_class_name, config FROM t_crud_page where domain_class_name = ?";

    private static final String INSERT_SQL = "insert into t_crud_page (`domain_class_name`, `config`) VALUES (?, ?)";

    private static final String UPDATE_SQL = "UPDATE t_crud_page set config = ? where domain_class_name = ?";

    private static final String DELETE_SQL = "DELETE FROM t_crud_page where  domain_class_name = ?";

    private static final String SELECT_MULTIPLE_SQL = "SELECT domain_class_name, config FROM t_crud_page where domain_class_name like concat('%',?, '%')";

    @Override
    public Optional<CrudPage> get(Class<?> domainClass) {
        try {
            StringCrudPage stringCrudPage = jdbcTemplate.queryForObject(SELECT_ONE_SQL, new BeanPropertyRowMapper<>(StringCrudPage.class), getDomainName(domainClass));
            return Optional.ofNullable(stringCrudPage);
        } catch (EmptyResultDataAccessException | BadSqlGrammarException e) {
            log.warn("data base table row t_crud_page.{} not created", getDomainName(domainClass));
            initTable();
            log.info("data base table row t_crud_page.{} create finish", getDomainName(domainClass));
            return Optional.empty();
        }

    }

    private String getDomainName(Class<?> domainClass) {
        return DomainMappingManager.MappingSupport.getDomainName(domainClass);
    }

    @Override
    public void put(Class<?> domainClass, CrudPage crudPage) {
        Optional<CrudPage> pageOptional = get(domainClass);
        if (pageOptional.isPresent()) {
            jdbcTemplate.update(UPDATE_SQL, crudPage.getConfig(), getDomainName(domainClass));
        } else {
            jdbcTemplate.update(INSERT_SQL, getDomainName(domainClass), crudPage.getConfig());
        }
    }

    @Override
    public void delete(Class<?> domainClass) {
        jdbcTemplate.update(DELETE_SQL, getDomainName(domainClass));
    }

    @Override
    public Page<CrudPage> findAll(String domain, Pageable pageable) {
        List<? extends CrudPage> content = getContent(domain, pageable.getPageNumber(), pageable.getPageSize());
        return new PageImpl<>((List<CrudPage>) content, pageable, getTotalNum(domain));
    }

    private Long getTotalNum(String domain) {
        String totalNumSql = String.format("select count(*) from (%s) a", SELECT_MULTIPLE_SQL);
        return jdbcTemplate.queryForObject(totalNumSql, Long.class, domain);
    }

    private List<? extends CrudPage> getContent(String domain, int page, int pageRow) {
        String limitSql = String.format("%s limit ?, ?", SELECT_MULTIPLE_SQL);
        return jdbcTemplate.query(limitSql, new BeanPropertyRowMapper<>(StringCrudPage.class), domain, page * pageRow, pageRow);
    }

    @Override
    public int getOrder() {
        return 100;
    }


    private void initTable() {
        jdbcTemplate.execute(INIT_TABLE_SQL);
    }
}
